<?php

namespace Hangpu888\Hpadmin;

use Exception;
use Hangpu888\Hpadmin\utils\Json;
use PDO;

/**
 * @title HPAdmin数据导入安装
 * @desc 控制器描述
 * @author 楚羽幽 <admin@hangpu.net>
 */
class InstallController
{
    /**
     * 检测是否已有安装文件
     *
     * @return void
     */
    public function index()
    {
        if (request()->method() == 'POST') {
            $filePath = base_path() . '/vendor/hangpu888/hpadmin/src/view/install/static/agreement.txt';
            $agreement = file_get_contents($filePath);
            return Json::successRes(['content' => $agreement]);
        } else {
            return Json::fail('请求类型错误');
        }
    }

    /**
     * 第1步：环境检测
     *
     * @return void
     */
    public function step1()
    {
        $server = $this->getServer();
        $fun = $this->checkFun();
        $dirAuth = $this->dirAuth();
        $data = [
            'server'                => $server,
            'fun'                   => $fun,
            'dir'                   => $dirAuth
        ];
        return Json::successRes($data);
    }

    /**
     * 第2步：安装配置
     *
     * @return void
     */
    public function step2()
    {
        if (request()->method() == 'PUT') {
            try {
                // 获取数据
                $post = request()->post();
                // 链接数据库
                $databasePost = isset($post['database']) ? $post['database'] : '';
                $sitePost = isset($post['site']) ? $post['site'] : '';
                $adminPost = isset($post['admin']) ? $post['admin'] : '';
                // 数据验证
                if (!isset($databasePost[0]['value']) || empty($databasePost[0]['value'])) {
                    throw new Exception('请选择数据库类型');
                }
                if (!isset($databasePost[1]['value']) || empty($databasePost[1]['value'])) {
                    throw new Exception('请输入数据库地址');
                }
                if (!isset($databasePost[2]['value']) || empty($databasePost[2]['value'])) {
                    throw new Exception('请输入数据库端口');
                }
                if (!isset($databasePost[3]['value']) || empty($databasePost[3]['value'])) {
                    throw new Exception('请输入数据库用户');
                }
                if (!isset($databasePost[4]['value']) || empty($databasePost[4]['value'])) {
                    throw new Exception('请输入数据库名称');
                }
                if (!isset($databasePost[5]['value']) || empty($databasePost[5]['value'])) {
                    throw new Exception('请输入数据库密码');
                }
                if (!isset($databasePost[6]['value']) || empty($databasePost[6]['value'])) {
                    throw new Exception('请输入字符集');
                }
                if (!isset($databasePost[7]['value']) || empty($databasePost[7]['value'])) {
                    throw new Exception('请输入表前缀');
                }
                // 站点数据安装验证
                if (!isset($sitePost[0]['value']) || empty($sitePost[0]['value'])) {
                    throw new Exception('请输入应用名称');
                }
                if (!isset($sitePost[1]['value']) || empty($sitePost[1]['value'])) {
                    throw new Exception('请输入应用域名');
                }
                // 管理员验证
                if (!isset($adminPost[0]['value']) || empty($adminPost[0]['value'])) {
                    throw new Exception('请输入登录账号');
                }
                if (strlen($adminPost[0]['value']) < 5 || strlen($adminPost[0]['value']) > 16) {
                    throw new Exception('登录账号5-16个字符');
                }
                if (!isset($adminPost[1]['value']) || empty($adminPost[1]['value'])) {
                    throw new Exception('请输入登录密码');
                }
                if (strlen($adminPost[1]['value']) < 6 || strlen($adminPost[0]['value']) > 20) {
                    throw new Exception('登录密码6-20位数字');
                }
                if (!isset($adminPost[2]['value']) || empty($adminPost[2]['value'])) {
                    throw new Exception('请再次输入登录密码');
                }
                if ($adminPost[1]['value'] != $adminPost[2]['value']) {
                    throw new Exception('两次密码输入不一致');
                }

                // 连接数据库
                $connectDB = $this->connectDB($databasePost);
                // 数据库前缀
                $prefix = isset($post['database'][7]['value']) ? trim($post['database'][7]['value']) : '';
                // 检测表是否为空
                $result = $connectDB->query("SHOW TABLES LIKE '{$prefix}admin'");
                $row = $result->fetchAll();
                if (count($row) == 1) {
                    throw new Exception('该数据库表已存在或已安装');
                }
                return Json::successRes($post);
            } catch (\Throwable $e) {
                return Json::fail($e->getMessage());
            }
        } else {
            $data = [
                'mysqlType'             => $this->getMysqlType(),
                'mysqlList'             => $this->getMysqlConfig(),
                'site'                  => $this->getSiteInfo(),
                'admin'                 => $this->getAdminConfig(),
            ];
            return Json::successRes($data);
        }
    }

    /**
     * 第3步：执行数据安装操作
     *
     * @return void
     */
    public function step3()
    {
        // 数据库连接
        try {
            // 获取数据
            $act = request()->post('act');
            $post = request()->post('data');
            // 开始连接数据库
            $databasePost = isset($post['database']) ? $post['database'] : '';
            $connectDB = $this->connectDB($databasePost);
        } catch (\Throwable $e) {
            return Json::fail("数据库连接失败：{$e->getMessage()}");
        }
        try {
            // 当前日期
            $date = date("Y-m-d H:i:s");
            // 表前缀
            $prefix = isset($databasePost[7]['value']) ? trim($databasePost[7]['value']) : '';
            switch ($act) {
                    // 写入用户
                case 'admin_add':
                    // 获取管理员参数
                    $username = isset($post['admin'][0]['value']) ? $post['admin'][0]['value'] : '';
                    $userPwd = isset($post['admin'][1]['value']) ? md5($post['admin'][1]['value']) : '';
                    $adminSql = "INSERT INTO `{$prefix}admin` VALUES (1,'{$date}','{$date}',1,'{$username}','{$userPwd}',1,'系统管理员','0',NULL,NULL,'');";
                    $connectDB->query("{$adminSql}");
                    break;
                    // 写入配置项
                case 'webconfig_add':
                    $web_name = isset($post['site'][0]['value']) ? $post['site'][0]['value'] : '';
                    $web_url = isset($post['site'][1]['value']) ? $post['site'][1]['value'] : '';
                    $configSql = "";
                    $configSql .= "INSERT INTO `{$prefix}webconfig` VALUES (1, '{$date}', '{$date}', 1, '应用名称', 'web_name', '{$web_name}', 'input', '', '', 100);";
                    $configSql .= "INSERT INTO `{$prefix}webconfig` VALUES (2, '{$date}', '{$date}', 1, '应用域名', 'web_url', '{$web_url}', 'input', '', '', 100);";
                    $connectDB->query("{$configSql}");
                    break;
                    // 数据结构安装
                default:
                    $sqlArray = $this->strReplace((string) $act, (string) $prefix);
                    // 执行SQL
                    foreach ($sqlArray as $sql) {
                        $connectDB->query("{$sql};");
                    }
                    break;
            }
            // 返回操作信息
            $text = "{$act} 安装成功...";
            if ($act == 'admin_add') {
                $this->installRemote();
                $this->installEnv($post);
                return Json::successFul($text, ['status' => 'ok']);
            } else {
                return Json::successFul($text, ['status' => 'no']);
            }
        } catch (\Throwable $e) {
            return Json::fail($e->getMessage());
        }
    }

    /**
     * 安装静态远程文件
     *
     * @return void
     */
    private function installRemote()
    {
        $targetDir = base_path()."/public/resources/";
        $sourcePath = base_path()."/vendor/hangpu888/hpadmin/src/view/admin/resources/";
        $this->copy_to_file($sourcePath,$targetDir);
    }

    /**
     * 安装配置文件
     *
     * @param array $post
     * @return void
     */
    private function installEnv(array $post)
    {
        // 数据库获取参数
        $type = isset($post['database'][0]['value']) ? $post['database'][0]['value'] : '';
        $host = isset($post['database'][1]['value']) ? $post['database'][1]['value'] : '';
        $database = isset($post['database'][4]['value']) ? trim($post['database'][4]['value']) : '';
        $port = isset($post['database'][2]['value']) ? $post['database'][2]['value'] : '';
        $user = isset($post['database'][3]['value']) ? trim($post['database'][3]['value']) : '';
        $password = isset($post['database'][5]['value']) ? trim($post['database'][5]['value']) : '';
        $charset = isset($post['database'][6]['value']) ? trim($post['database'][6]['value']) : '';
        $prefix = isset($post['database'][7]['value']) ? trim($post['database'][7]['value']) : '';
        // 当前目录
        $currentDir = str_replace('\\', '/', __DIR__);
        // 拼接配置文件路径
        $currentEnvPath = "{$currentDir}/data/env.tpl";
        $envPath = base_path() . "/.env";

        // 读取配置文件
        $envConfig = file_get_contents($currentEnvPath);

        // 替换配置文件参数
        $str1 = ["{type}", "{host}", "{database}", "{username}", "{password}", "{port}", "{charset}", "{prefix}"];
        $str2 = [$type, $host, $database, $user, $password, $port, $charset, $prefix];
        $envConfig = str_replace($str1, $str2, $envConfig);

        // 写入配置文件
        return file_put_contents($envPath, $envConfig);
    }

    /**
     * 替换SQL文件内容
     *
     * @param string $filename
     * @param string $prefix
     * @return array
     */
    private function strReplace(string $filename, string $prefix): array
    {
        $currentDir = str_replace('\\', '/', __DIR__);
        $sqlPath = "{$currentDir}/data/sql/{$filename}.sql";
        if (!file_exists($sqlPath)) {
            throw new Exception("找不到{$filename}数据库文件");
        }
        // 替换SQL模板文件内容
        $_sql = file_get_contents($sqlPath);
        $_sql = str_replace("php_", $prefix, $_sql);
        $_arr = explode(';', $_sql);
        $_arr = array_filter($_arr);
        return $_arr;
    }

    /**
     * 第4步：安装完成
     *
     * @return void
     */
    public function step4()
    {
        try {
            // 跳转域名
            $http           = isset($_SERVER['REQUEST_SCHEME']) ? $_SERVER['REQUEST_SCHEME'] : 'http';
            $host           = request()->host();
            $domain         = "{$http}://{$host}";
            $str = <<<str
            <h3>恭喜您，安装完成！</h3>
            <b style="color:red;">请重启服务Webman服务</b>
            str;
            $data = [
                'content'           => $str,
                'adminUrl'          => "{$domain}/admin",
            ];
            return Json::successFul('success', $data);
        } catch (\Throwable $e) {
            return Json::fail($e->getMessage());
        }
    }


    /**
     * 站点表单信息
     *
     * @return void
     */
    private function getSiteInfo()
    {
        $web_name       = "HPAdmin";
        $http           = isset($_SERVER['REQUEST_SCHEME']) ? $_SERVER['REQUEST_SCHEME'] : 'http';
        $host           = request()->host();
        $web_url        = "{$http}://{$host}";
        $data           = [
            [
                'field'             => 'web_name',
                'type'              => 'text',
                'name'              => '应用名称',
                'value'             => $web_name,
                'tip'               => '示例：HPAdmin 或 行铺网络',
            ],
            [
                'field'             => 'web_url',
                'type'              => 'text',
                'name'              => '应用域名',
                'value'             => $web_url,
                'tip'               => '示例：http://hpadmin.hangpu.net，结尾不带斜杠，接口处可能经常用到',
            ],
        ];
        return $data;
    }

    /**
     * 管理员配置
     *
     * @return void
     */
    private function getAdminConfig()
    {
        $data = [
            [
                'field'             => 'username',
                'type'              => 'text',
                'name'              => '登录账号',
                'value'             => '',
                'tip'               => '后台系统管理员账号，不允许删除',
            ],
            [
                'field'             => 'password',
                'type'              => 'password',
                'name'              => '登录密码',
                'value'             => '',
                'tip'               => '系统管理员登录密码',
            ],
            [
                'field'             => 'confirm_pass',
                'type'              => 'password',
                'name'              => '重复密码',
                'value'             => '',
                'tip'               => '请在一次输入登录密码',
            ],
        ];
        return $data;
    }

    /**
     * 获取数据库配置参数
     *
     * @return void
     */
    private function getMysqlConfig()
    {
        $data = [
            [
                'field'             => 'type',
                'name'              => '数据库类型',
                'value'             => 'mysql',
            ],
            [
                'field'             => 'host',
                'name'              => '数据库地址',
                'value'             => '127.0.0.1',
            ],
            [
                'field'             => 'port',
                'name'              => '数据库端口',
                'value'             => '3306',
            ],
            [
                'field'             => 'user',
                'name'              => '数据库用户',
                'value'             => '',
            ],
            [
                'field'             => 'database',
                'name'              => '数据库名称',
                'value'             => '',
            ],
            [
                'field'             => 'password',
                'name'              => '数据库密码',
                'value'             => '',
            ],
            [
                'field'             => 'charset',
                'name'              => '数据库字符',
                'value'             => 'utf8',
            ],
            [
                'field'             => 'prefix',
                'name'              => '数据表前缀',
                'value'             => 'php_',
            ],
        ];
        return $data;
    }

    /**
     * 获取数据库类型
     *
     * @return void
     */
    private function getMysqlType()
    {
        $data = [
            [
                'name'              => 'mysql',
                'value'             => 'mysql',
            ],
        ];
        return $data;
    }

    /**
     * 连接数据库
     *
     * @param array $data
     * @return PDO
     */
    private function connectDB(array $data): PDO
    {
        // 获取参数
        $type = isset($data[0]['value']) ? $data[0]['value'] : '';
        $host = isset($data[1]['value']) ? $data[1]['value'] : '';
        $database = isset($data[4]['value']) ? trim($data[4]['value']) : '';
        $port = isset($data[2]['value']) ? $data[2]['value'] : '';
        $user = isset($data[3]['value']) ? trim($data[3]['value']) : '';
        $password = isset($data[5]['value']) ? trim($data[5]['value']) : '';

        // 连接数据库
        $dsn = "{$type}:dbname={$database};host={$host};port={$port};";
        $con = new PDO($dsn, $user, $password);
        return $con;
    }


    /**
     * 检测函数是否启用
     *
     * @return void
     */
    private function checkFun()
    {
        $data = [
            [
                'name'              => 'GD库',
                'result'            => get_extension_funcs("gd") ? true : false,
                'value'             => get_extension_funcs("gd") ? '已开启' : '未开启',
            ],
        ];
        return $data;
    }

    /**
     * 检测目录权限
     *
     * @return void
     */
    private function dirAuth()
    {
        $dirAuth = [
            [
                'path'              => base_path() . "/public",
                'name'              => "/public",
            ],
            [
                'path'              => base_path() . "/runtime",
                'name'              => "/runtime",
            ],
            [
                'path'              => base_path() . "/vendor",
                'name'              => "/vendor",
            ],
        ];
        $data = [];
        foreach ($dirAuth as $key => $value) {
            $data[$key]['name'] = $value['name'];
            $data[$key]['result'] = is_writable($value['path']) ? true : false;
            $data[$key]['value'] = is_writable($value['path']) ? '可写入' : '不可写入';
        }
        return $data;
    }

    /**
     * 获得服务器信息
     *
     * @return void
     */
    private function getServer()
    {
        $host = request()->host();
        $data = [
            [
                'name'              => '应用域名',
                'result'            => true,
                'value'             => "",
                'value'             => "http://{$host}",
            ],
            [
                'name'              => '操作系统',
                'result'            => true,
                'value'             => PHP_OS,
            ],
            [
                'name'              => '编译引擎',
                'result'            => true,
                'value'             => '',
                // 'value'             => $server['SERVER_SOFTWARE'],
            ],
            [
                'name'              => 'PHP版本',
                'result'            => !version_compare(PHP_VERSION, '7.2.5', '<'),
                'value'             => PHP_VERSION . "（推荐8.0，最低7.4.0）",
            ],
            [
                'name'              => '安装目录',
                'result'            => true,
                'value'             => str_replace('\\', '/', base_path()),
            ],
        ];
        return $data;
    }


    /**
     * 获得目录下的所有文件路径并复制到指定的目录下面
     * $old_dir：目标文件目录 $new_dir：需要复制到的文件目录，$quanxian：设置权限
     *
     * @param string $old_dir
     * @param string $new_dir
     * @param integer $quanxian
     * @return void
     */
    private function copy_to_file(string $old_dir, string $new_dir, $quanxian = 0755)
    {
        if (!is_dir($new_dir)) { //判断有没有目录，没有则创建
            @mkdir($new_dir, $quanxian, true);
        }
        $res = '';
        $temp = scandir($old_dir);
        if (is_array($temp) && count($temp) > 2) {
            unset($temp[0], $temp[1]);
            foreach ($temp as $key => $val) {
                $file_url = $old_dir . DIRECTORY_SEPARATOR . $val;
                $xin_dir = $new_dir . DIRECTORY_SEPARATOR . $val; //组件新的目录
                if (is_dir($file_url)) { //是否是目录
                    $this->copy_to_file($file_url, $xin_dir);
                } elseif (is_file($file_url)) {
                    $res = copy($file_url, $xin_dir);
                }
            }
        }
        return $res;
    }
}
